#!/usr/bin/env python
# encoding: utf-8

import logging.config
import os
from xmitools.util.subprocess_helper import run_in_subprocess

log = logging.getLogger('xbaydns.util.image')

IMG_MNT = '/mnt/xmi-mnt'
SRC_IMG_MNT = '/mnt/xmi-src-mnt'
PATH_DELIMITER='/'

DEFAULT_FSTAB ='''# Default /etc/fstab
/dev/sda1 /     ext3    defaults 1 1
/dev/sdb  /mnt  ext3    defaults 0 0
none      /proc proc    defaults 0 0
none      /sys  sysfs   defaults 0 0'''

LEGACY_FSTAB = '''# Legacy /etc/fstab
/dev/sda1 /     ext3    defaults 1 1
/dev/sda2 /mnt  ext3    defaults 0 0

/dev/sda3 swap  swap    defaults 0 0
none      /proc proc    defaults 0 0
none      /sys  sysfs   defaults 0 0'''

PATHS = os.environ['PATH'].split(':')

def fix_path(bin):
    if bin.startswith('/'):
        return True, bin
    
    found = False
    tmp_path = bin
    for path in PATHS:
        tmp_path = os.path.join(path, bin)
        if os.path.exists(tmp_path):
            found = True
            break
    return found, tmp_path


def echo(cmd):
    os.system('echo %s' %cmd)

def execute(cmd, silent, no_fail, prompt='', timeout = 0, use_env = True, crack_cmd = True):
    if prompt != '':
        echo(prompt)
    
    if use_env:
        if crack_cmd:
            cmd_seq = cmd.split(' ')
            bin_name = cmd_seq[0]
            found, cmd_seq[0] = fix_path(bin_name)
        else:
            nouse, cmd[0] = fix_path(cmd[0])
            cmd_seq = cmd
        
    code,out,err = run_in_subprocess(cmd_seq, timeout)
    
    #if not silent:
    print code
    print out
    print err
        
    if code > 0 and (not no_fail):
        print cmd_seq
        msg = 'Failed in doing:\t' + ' '.join(cmd_seq)
        if out == '' and err == '':
            detail = 'Detail not available.'
        elif err != '':
            detail = err
        raise ImageBuilderException(msg, detail)
    
def execs(cmd, no_fail, prompt='', timeout = 0, use_env = True, crack_cmd = True):
    execute(cmd, True, no_fail, prompt, timeout, use_env, crack_cmd)

def execn(cmd, slient, prompt='', timeout = 0, use_env = True, crack_cmd = True):
    execute(cmd, slient, True, prompt, timeout, use_env, crack_cmd)
    
def execsf(cmd, prompt = '', timeout = 0, use_env = True, crack_cmd = True):
    execute(cmd, True, False, prompt, timeout, use_env, crack_cmd)
    
def execsn(cmd, prompt='', timeout = 0, use_env = True, crack_cmd = True):
    execute(cmd, True, True, prompt, timeout, use_env, crack_cmd)

class ImageBuilderException:
    def __init__(self, msg, detail = 'Detail not available.'):
        self.msg = msg
        self.detail = detail

class ImageBuilder:

    def __init__(self, image_filename = '/home/zhangli/test.img',
                 image_size_mb = 1024,
                 image_format = 'ext3',
                 exclude_list = ['*.img'],
                 mount_point = IMG_MNT,
                 fstab = 'DEFAULT'):
        self.image_filename = image_filename
        self.image_size_mb = image_size_mb
        self.image_format = image_format
        self.exclude_list = exclude_list
        self.fstab = fstab
        self.mount_point = mount_point
        self.source_mount_point = SRC_IMG_MNT 
        
    def begin_prompt(self):
        pass
    
    def create_image_file(self, overwrite, force_umount):
        if os.path.exists(self.image_filename):
            if overwrite:
                echo('Image file %s already exists, overwrite it.' %self.image_filename)
                os.remove(self.image_filename)
            else:
                raise ImageBuilderException('Image file already exists.')
            
        mb_size = 1024 * 1024
        cmd = 'dd if=/dev/zero of=%s bs=%d count=1 seek=%d' %(self.image_filename, mb_size, self.image_size_mb - 1)
        execsf(cmd)
        if os.path.ismount(self.mount_point):
            if force_umount:
                echo('Target mount point %s already occupied, force umount it.' %self.mount_point)
                self.umount_image()
            else:
                raise ImageBuilderException('%s is already mounted.' %mount_point)
            
    def format_image_file(self):
        execsf('/sbin/mkfs -t %s -F %s' %(self.image_format, self.image_filename))
        
    def mount_image(self):
        if not os.path.isdir(self.mount_point):
            os.mkdir(self.mount_point)
        if not os.path.ismount(self.mount_point):
            execsf('mount %s %s -o loop -t %s' %(self.image_filename, self.mount_point, self.image_format))
            
    def umount_image(self):
        if os.path.ismount(self.mount_point):
            execsf('umount -d %s' %self.mount_point)
            
    def initial_image(self):
        dirs = ['mnt', 'proc', 'sys', 'dev']
        full_dirs = [os.path.join(self.mount_point, d) for d in dirs]
    
        for dir in full_dirs:
            if not os.path.isdir(dir):
                os.mkdir(dir)
    
        dev_dir = os.path.join(self.mount_point, 'dev')
        
        nodes_map = {'null': 'c 1 3',
                     'zero': 'c 1 5',
                     'tty': 'c 5 0',
                     'console': 'c 5 1'}
        
        for n in nodes_map:
            path = os.path.join(dev_dir, n)
            if not os.path.exists(path):
                execsf('mknod %s %s' %(path, nodes_map[n]))
                
        path = os.path.join(dev_dir, 'X0R')
        if not os.path.exists(path):            
            execsf('ln -s null %s' %path)

    def do_copy(self, src, dst, exclusions):
        
#下面被注释的代码会有奇怪的行为，除了rsync中要copy的文件外，还会copy 当前文件(image.py)文件夹下的文件 
#如果不是这个问题，倒是想用这段代码的
#        exclude = ''
#        for e in exclusions:
#            exclude += ' --exclude "%s"' %e
#            
#        cmd = 'rsync -a -r -l -S %s %s %s' %(exclude, src + '/', dst)
#        print cmd
#        execsf(cmd)

#注意 rsync /scr /dest 并不会copy scr 下的文件到dest
#得用rsync /scr/ /dest 才行
        exclude = []
        for e in exclusions:
            exclude.extend(["--exclude", e])
            
        cmd_seq = ['rsync', '-a', '-r', '-l', '-S']
        cmd_seq.extend(exclude)
        cmd_seq.extend([src + '/', dst])
        execsf(cmd_seq, crack_cmd = False)
    
    def before_copy_content(self):
        pass
    
    def copy_content(self):
        pass
    
    def gen_fstab(self, type):
        if type == 'LEGACY':
            return LEGACY_FSTAB
        else:
            return DEFAULT_FSTAB

    def update_fstab(self, type):
        fstab_dir = os.path.join(self.mount_point, 'etc')
        fstab_file = os.path.join(fstab_dir, 'fstab')
        fstab_old = os.path.join(fstab_dir, 'fstab.old')
        if os.path.isfile(fstab_file):
            execsf('mv %s %s' %(fstab_file, fstab_old))
        if not os.path.isdir(fstab_dir):
            os.makedirs(fstab_dir)
        fstab_content = self.gen_fstab(type)
        fsth = open(fstab_file, 'w')
        fsth.writelines(fstab_content)
        fsth.write('\n')
        fsth.close()


    def build(self):
        try:            
            self.begin_prompt()
            self.create_image_file(overwrite = True, force_umount = True)
            self.format_image_file()
            self.mount_image()
            self.initial_image()
            self.before_copy_content()
            self.copy_content()
            self.update_fstab(self.fstab)
        except ImageBuilderException, e:
            echo(e.msg)
            echo(e.detail)
            self.on_fail()
#        except:
#            echo('ERROR ocurred, operation terminated.')
        else:
            self.on_finish()
                 
    def on_fail(self):        
        self.umount_image()
          
    def on_finish(self):
        pass

              
class VolImageBuilder(ImageBuilder):
    def __init__(self, image_filename = '/home/zhangli/test.img',
                 volume_path = '/home/zhangli/fonts',
                 image_size_mb = 1024,
                 image_format = 'ext3',
                 exclude_list = ['*.img'],
                 mount_point = IMG_MNT,
                 fstab = 'DEFAULT'):
        
        self.volume_path = volume_path
        ImageBuilder.__init__(self, image_filename,
                 image_size_mb,
                 image_format,
                 exclude_list,
                 mount_point,
                 fstab)
    
    def copy_content(self):
        self.do_copy(self.volume_path, self.mount_point, self.exclude_list)

class ImageCopier(ImageBuilder):
    def __init__(self, image_filename = '/home/zhangli/test.img',
                 image_source = '/home/zhangli/testsrc.img',
                 source_mount_point = SRC_IMG_MNT,
                 source_image_format = 'ext3',
                 image_size_mb = 1024,
                 image_format = 'ext3',
                 exclude_list = ['*.img'],
                 mount_point = IMG_MNT,
                 fstab = 'DEFAULT'):
        
        self.image_source = image_source
        self.source_mount_point = source_mount_point
        self.source_image_format = source_image_format
        ImageBuilder.__init__(self, image_filename,
                 image_size_mb,
                 image_format,
                 exclude_list,
                 mount_point,
                 fstab)
        
    def mount_source_image(self, force_umount):
        if not os.path.isdir(self.source_mount_point):
            os.mkdir(self.source_mount_point)
            
        if os.path.ismount(self.source_mount_point):
            if force_umount:
                execsf('umount -d %s' %self.source_mount_point)
            else:
                raise ImageBuilderException('Source image mount point occupied.')
        
        cmd = 'mount %s %s -o loop -t %s' %(self.image_source, self.source_mount_point, self.source_image_format)
        execsf(cmd)

    def before_copy_content(self):
        echo('begin pre-action for copy content')
        self.mount_source_image(force_umount = True)
    
    def copy_content(self):
        self.do_copy(self.source_mount_point, self.mount_point, self.exclude_list)
    
    def umount_source_image(self):
        if os.path.ismount(self.source_mount_point):
              execsf('umount -d %s' %self.source_mount_point)
        
    def on_fail(self):        
        self.umount_source_image()
        ImageBuilder.on_fail(self)
        
    def on_finish(self):
        self.umount_source_image()
    
class YumImageBuilder(ImageBuilder):
    def __init__(self, image_filename = '/home/zhangli/test.img',
                 image_size_mb = 1024,
                 image_format = 'ext3',
                 exclude_list = ['*.img'],
                 mount_point = IMG_MNT,
                 fstab = 'DEFAULT'):
        
        ImageBuilder.__init__(self, image_filename,
                 image_size_mb,
                 image_format,
                 exclude_list,
                 mount_point,
                 fstab)
    
    def copy_content(self):
        cmd = 'yum groupinstall Base -y --installroot %s' %self.mount_point
        execsf(cmd)
          
if __name__ == '__main__':
#    try:
#        execsf('mkdir /home/zhangli/mktest')
#    except ImageBuilderException, e:
#        print e.msg
#        print e.detail


#    imgsrc = VolImageBuilder()
#    imgsrc.build()

#    execsf('/sbin/mkfs -t ext3 -F /home/zhangli/test.img')
#    execsf('mkdir /home/zhangli/mktest')
#    cmd1 = ['/sbin/mkfs', '-t','ext3', '-F', '/home/zhangli/test.img']
#    cmd2 = ['mkdir', '/home/zhangli/mktest']
#    code, out, err = run_in_subprocess(cmd1, 0)
#    print code, out, err
    
    #execsf(cmd1)

        
#    os.system('cp /home/zhangli/test.c /mnt/xmi-mnt/')
#    os.system('rm /home/zhangli/testsrc.img')
#    os.system('cp /home/zhangli/test.img /home/zhangli/testsrc.img')
    img = ImageCopier()
    img.build()

    

